package service

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"gitcode.com/G-YT/iHealth25/server/global"
	"gitcode.com/G-YT/iHealth25/server/model/order"
	"gitcode.com/G-YT/iHealth25/server/model/user"
	wx_global "gitcode.com/G-YT/iHealth25/server/plugin/weixin/global"
	"gitcode.com/G-YT/iHealth25/server/plugin/weixin/model"
	"gitcode.com/G-YT/iHealth25/server/plugin/weixin/utils"
	"github.com/wechatpay-apiv3/wechatpay-go/core"
	"github.com/wechatpay-apiv3/wechatpay-go/core/notify"
	"github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi"
	"go.uber.org/zap"
	"net/http"
	"strings"
	"time"
)

type WxPayService struct{}

// PaymentTransaction 微信支付回调交易信息
type PaymentTransaction struct {
	OutTradeNo    *string `json:"out_trade_no"`
	TransactionId *string `json:"transaction_id"`
	TradeState    *string `json:"trade_state"`
	SuccessTime   *string `json:"success_time"`
	Amount        *struct {
		Total    *int    `json:"total"`
		Currency *string `json:"currency"`
	} `json:"amount"`
	Payer *struct {
		Openid *string `json:"openid"`
	} `json:"payer"`
}

// CreateJSAPIOrder 创建JSAPI支付订单
func (e *WxPayService) CreateJSAPIOrder(req model.JSAPIPayRequest) (payResponse *model.JSAPIPayResponse, err error) {
	// 参数验证
	if req.OrderID == 0 {
		return nil, errors.New("订单ID不能为空")
	}

	// 根据订单ID查询已存在的订单
	var existingOrder order.Order
	err = global.GVA_DB.Where("id = ?", req.OrderID).First(&existingOrder).Error
	if err != nil {
		if err.Error() == "record not found" {
			return nil, errors.New("订单不存在")
		}
		return nil, errors.New("查询订单失败")
	}

	// 检查订单状态，只有待支付的订单才能发起支付
	if existingOrder.Status != order.OrderStatusPending {
		statusMap := map[uint]string{
			order.OrderStatusPending:   "待支付",
			order.OrderStatusPaid:      "已支付",
			order.OrderStatusCancelled: "已取消",
			order.OrderStatusRefunded:  "已退款",
		}
		currentStatus := statusMap[existingOrder.Status]
		if currentStatus == "" {
			currentStatus = fmt.Sprintf("未知状态(%d)", existingOrder.Status)
		}
		return nil, errors.New("订单状态不正确，只能对待支付订单发起支付")
	}

	// 检查订单金额
	if existingOrder.Amount == nil || *existingOrder.Amount <= 0 {
		return nil, errors.New("订单金额无效")
	}

	// 处理OpenID逻辑
	var openID string
	if req.OpenID != "" {
		// 验证OpenID格式
		if len(req.OpenID) < 10 {
			return nil, errors.New("OpenID格式不正确")
		}
		// 如果请求中有OpenID，直接使用
		openID = req.OpenID
	} else if req.JsCode != "" {
		// 验证JsCode格式
		if len(req.JsCode) < 10 {
			return nil, errors.New("JsCode格式不正确")
		}
		// 如果没有OpenID但有JsCode，通过JsCode获取OpenID
		var err error
		openID, err = WechatService.GetOpenIDByCode(req.JsCode)
		if err != nil {
			return nil, errors.New("通过JsCode获取OpenID失败")
		}
		if openID == "" {
			return nil, errors.New("获取到的OpenID为空，请检查JsCode是否有效")
		}

		// 更新用户表中的OpenID
		if existingOrder.UserId != 0 {
			updateData := map[string]interface{}{
				"open_id": openID,
			}
			if updateErr := global.GVA_DB.Model(&user.User{}).Where("id = ?", existingOrder.UserId).Updates(updateData).Error; updateErr != nil {
				// 记录日志但不影响支付流程
				global.GVA_LOG.Error("更新用户OpenID失败", zap.Error(updateErr))
			}
		}
	} else {
		return nil, errors.New("OpenID和JsCode不能同时为空，请提供其中一个")
	}

	// 创建微信支付客户端
	client, err := utils.CreateWechatPayClient()
	if err != nil {
		return nil, errors.New("创建微信支付客户端失败")
	}

	// 使用已存在订单的订单号
	orderNo := existingOrder.OutTradeNo
	if orderNo == "" {
		// 如果订单号为空，生成新的订单号
		snowflakeID := utils.GenerateSnowflakeID()
		orderNo = fmt.Sprintf("iH%d", snowflakeID)
	}

	// 构建JSAPI支付请求
	svc := jsapi.JsapiApiService{Client: client}

	// 设置通知URL
	notifyURL := req.NotifyURL
	if notifyURL == "" {
		notifyURL = wx_global.WxPayConfig.NotifyUrl
	}

	// 构建商品描述
	description := *existingOrder.ProductName
	if req.Description != "" {
		description = req.Description
	}

	// 计算支付金额（转换为分）
	totalAmount := int64(*existingOrder.Amount * 100)

	// 构建支付请求
	prepayReq := jsapi.PrepayRequest{
		Appid:       core.String(wx_global.WxPayConfig.AppID),
		Mchid:       core.String(wx_global.WxPayConfig.MchID),
		Description: core.String(description),
		OutTradeNo:  core.String(orderNo),
		NotifyUrl:   core.String(notifyURL),
		Amount: &jsapi.Amount{
			Total: core.Int64(totalAmount),
		},
		Payer: &jsapi.Payer{
			Openid: core.String(openID),
		},
	}

	// 设置附加数据
	if req.Attach != "" {
		prepayReq.Attach = core.String(req.Attach)
	}

	// 调用微信支付API获取prepay_id和调起支付参数
	ctx := context.Background()
	resp, result, err := svc.PrepayWithRequestPayment(ctx, prepayReq)
	if err != nil {
		global.GVA_LOG.Error("JSAPI支付下单失败", zap.Error(err), zap.String("orderNo", orderNo))
		// 检查是否是网络错误
		if strings.Contains(err.Error(), "timeout") {
			return nil, errors.New("微信支付服务超时，请稍后重试")
		}
		if strings.Contains(err.Error(), "connection") {
			return nil, errors.New("网络连接失败，请检查网络状态")
		}
		return nil, errors.New("JSAPI支付下单失败")
	}

	// 检查响应状态
	if result != nil && result.Response != nil && result.Response.StatusCode != 200 {
		global.GVA_LOG.Error("微信支付API返回错误状态", zap.Int("statusCode", result.Response.StatusCode))
		return nil, errors.New("微信支付服务返回错误")
	}

	// 验证响应数据
	if resp == nil {
		return nil, errors.New("微信支付API返回空响应")
	}
	if resp.Appid == nil || resp.TimeStamp == nil || resp.NonceStr == nil || resp.Package == nil || resp.SignType == nil || resp.PaySign == nil {
		return nil, errors.New("微信支付API返回数据不完整")
	}

	// 构建响应
	payResponse = &model.JSAPIPayResponse{
		AppID:     *resp.Appid,
		TimeStamp: *resp.TimeStamp,
		NonceStr:  *resp.NonceStr,
		Package:   *resp.Package,
		SignType:  *resp.SignType,
		PaySign:   *resp.PaySign,
		OrderID:   orderNo,
	}

	// 更新订单的微信支付相关信息
	updateData := map[string]interface{}{
		"openid":           openID,
		"out_trade_no":     orderNo,
		"total":            int(totalAmount),
		"currency":         "CNY",
		"trade_state":      "NOTPAY",
		"trade_type":       "JSAPI",
		"appid":            wx_global.WxPayConfig.AppID,
		"mchid":            wx_global.WxPayConfig.MchID,
		"payer_currency":   "CNY",
		"trade_state_desc": "未支付",
		"updated_at":       time.Now(),
	}

	if req.Attach != "" {
		updateData["attach"] = req.Attach
	}

	// 使用事务确保数据一致性
	tx := global.GVA_DB.Begin()
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
			global.GVA_LOG.Error("更新订单信息时发生panic", zap.Any("error", r))
		}
	}()

	// 再次检查订单状态，防止并发修改
	var currentOrder order.Order
	err = tx.Where("id = ? AND status = ?", req.OrderID, order.OrderStatusPending).First(&currentOrder).Error
	if err != nil {
		tx.Rollback()
		if err.Error() == "record not found" {
			return nil, errors.New("订单状态已变更，无法发起支付")
		}
		return nil, errors.New("检查订单状态失败")
	}

	// 更新订单信息
	err = tx.Model(&order.Order{}).Where("id = ?", req.OrderID).Updates(updateData).Error
	if err != nil {
		tx.Rollback()
		global.GVA_LOG.Error("更新订单微信支付信息失败", zap.Error(err), zap.Uint("orderID", req.OrderID))
		return nil, errors.New("更新订单信息失败")
	}

	// 提交事务
	if err = tx.Commit().Error; err != nil {
		global.GVA_LOG.Error("提交订单更新事务失败", zap.Error(err))
		return nil, errors.New("保存订单信息失败")
	}

	global.GVA_LOG.Info("JSAPI支付订单创建成功", zap.Uint("orderID", req.OrderID), zap.String("orderNo", orderNo), zap.String("openID", openID))
	return payResponse, nil
}

// HandlePaymentNotify 处理微信支付回调
func (e *WxPayService) HandlePaymentNotify(body []byte, headers map[string]string) error {
	// 记录开始处理
	global.GVA_LOG.Info("开始处理微信支付回调",
		zap.Int("body_size", len(body)),
		zap.String("timestamp", headers["Wechatpay-Timestamp"]),
	)

	// 创建回调处理器
	handler := notify.NewNotifyHandler(wx_global.WxPayConfig.MchAPIv3Key, nil)

	// 构建HTTP请求
	req := &http.Request{
		Header: make(http.Header),
	}
	req.Header.Set("Wechatpay-Signature", headers["Wechatpay-Signature"])
	req.Header.Set("Wechatpay-Timestamp", headers["Wechatpay-Timestamp"])
	req.Header.Set("Wechatpay-Nonce", headers["Wechatpay-Nonce"])
	req.Header.Set("Wechatpay-Serial", headers["Wechatpay-Serial"])

	// 解析回调数据
	transaction := &PaymentTransaction{}
	var result interface{}
	notifyReq, err := handler.ParseNotifyRequest(context.Background(), req, &result)
	if err != nil {
		global.GVA_LOG.Error("解析微信回调数据失败",
			zap.Error(err),
			zap.String("timestamp", headers["Wechatpay-Timestamp"]),
		)
		return fmt.Errorf("解析回调数据失败: %v", err)
	}

	// 将解析结果转换为PaymentTransaction
	resultBytes, err := json.Marshal(result)
	if err != nil {
		global.GVA_LOG.Error("序列化解析结果失败", zap.Error(err))
		return fmt.Errorf("序列化解析结果失败: %v", err)
	}

	err = json.Unmarshal(resultBytes, transaction)
	if err != nil {
		global.GVA_LOG.Error("反序列化为PaymentTransaction失败", zap.Error(err))
		return fmt.Errorf("反序列化为PaymentTransaction失败: %v", err)
	}

	global.GVA_LOG.Info("微信回调签名验证成功",
		zap.String("timestamp", headers["Wechatpay-Timestamp"]),
	)

	// 验证关键字段是否存在
	if transaction.OutTradeNo == nil || *transaction.OutTradeNo == "" {
		global.GVA_LOG.Error("回调数据中缺少商户订单号",
			zap.String("timestamp", headers["Wechatpay-Timestamp"]),
		)
		return fmt.Errorf("回调数据中缺少商户订单号")
	}

	if transaction.TransactionId == nil || *transaction.TransactionId == "" {
		global.GVA_LOG.Error("回调数据中缺少微信支付订单号",
			zap.String("out_trade_no", *transaction.OutTradeNo),
			zap.String("timestamp", headers["Wechatpay-Timestamp"]),
		)
		return fmt.Errorf("回调数据中缺少微信支付订单号")
	}

	if transaction.TradeState == nil {
		global.GVA_LOG.Error("回调数据中缺少交易状态",
			zap.String("out_trade_no", *transaction.OutTradeNo),
			zap.String("transaction_id", *transaction.TransactionId),
			zap.String("timestamp", headers["Wechatpay-Timestamp"]),
		)
		return fmt.Errorf("回调数据中缺少交易状态")
	}

	// 记录回调信息
	global.GVA_LOG.Info("收到微信支付回调",
		zap.String("out_trade_no", *transaction.OutTradeNo),
		zap.String("transaction_id", *transaction.TransactionId),
		zap.String("trade_state", string(*transaction.TradeState)),
		zap.String("timestamp", headers["Wechatpay-Timestamp"]),
	)

	// 更新订单状态
	err = e.updateOrderStatus(transaction)
	if err != nil {
		global.GVA_LOG.Error("更新订单状态失败",
			zap.Error(err),
			zap.String("out_trade_no", *transaction.OutTradeNo),
			zap.String("transaction_id", *transaction.TransactionId),
			zap.String("trade_state", string(*transaction.TradeState)),
		)
		return fmt.Errorf("更新订单状态失败: %v", err)
	}

	global.GVA_LOG.Info("订单状态更新成功",
		zap.String("out_trade_no", *transaction.OutTradeNo),
		zap.String("transaction_id", *transaction.TransactionId),
		zap.String("trade_state", string(*transaction.TradeState)),
	)

	// 保存回调记录
	err = e.savePaymentCallback(notifyReq, transaction)
	if err != nil {
		global.GVA_LOG.Error("保存回调记录失败",
			zap.Error(err),
			zap.String("out_trade_no", *transaction.OutTradeNo),
			zap.String("transaction_id", *transaction.TransactionId),
		)
		// 这里不返回错误，因为订单状态已经更新成功
	} else {
		global.GVA_LOG.Info("回调记录保存成功",
			zap.String("out_trade_no", *transaction.OutTradeNo),
			zap.String("transaction_id", *transaction.TransactionId),
		)
	}

	global.GVA_LOG.Info("微信支付回调处理完成",
		zap.String("out_trade_no", *transaction.OutTradeNo),
		zap.String("transaction_id", *transaction.TransactionId),
		zap.String("trade_state", string(*transaction.TradeState)),
	)

	return nil
}

// updateOrderStatus 更新订单状态
func (e *WxPayService) updateOrderStatus(transaction *PaymentTransaction) error {
	// 映射微信支付状态到系统订单状态
	var orderStatus uint
	var tradeStateDesc string
	var tradeStateInt int

	// 将微信支付状态字符串转换为整数
	if stateInt, exists := order.TradeStateStringMap[*transaction.TradeState]; exists {
		tradeStateInt = stateInt
	} else {
		tradeStateInt = order.TradeStatePayError // 默认为支付失败
	}

	switch *transaction.TradeState {
	case "SUCCESS":
		orderStatus = order.OrderStatusPaid
		tradeStateDesc = "支付成功"
	case "REFUND":
		orderStatus = order.OrderStatusRefunded
		tradeStateDesc = "转入退款"
	case "CLOSED":
		orderStatus = order.OrderStatusCancelled
		tradeStateDesc = "已关闭"
	case "USERPAYING":
		orderStatus = order.OrderStatusPending
		tradeStateDesc = "用户支付中"
	case "NOTPAY":
		orderStatus = order.OrderStatusPending
		tradeStateDesc = "未支付"
	default:
		orderStatus = order.OrderStatusPending
		tradeStateDesc = "支付失败"
	}

	// 构建更新数据
	updateData := map[string]interface{}{
		"status":           orderStatus,
		"trade_state":      tradeStateInt,
		"trade_state_desc": tradeStateDesc,
		"transaction_id":   *transaction.TransactionId,
	}

	// 如果支付成功，更新支付时间
	if *transaction.TradeState == "SUCCESS" {
		if transaction.SuccessTime != nil {
			// 解析微信时间格式 RFC3339
			payTime, err := time.Parse(time.RFC3339, *transaction.SuccessTime)
			if err != nil {
				global.GVA_LOG.Error("解析支付时间失败", zap.Error(err))
				// 使用当前时间作为支付时间
				payTime = time.Now()
			}
			updateData["pay_time"] = payTime
		} else {
			updateData["pay_time"] = time.Now()
		}
	}

	// 更新主订单表（ih_orders）
	err := global.GVA_DB.Model(&order.Order{}).Where("out_trade_no = ?", *transaction.OutTradeNo).Updates(updateData).Error
	if err != nil {
		return fmt.Errorf("更新主订单失败: %v", err)
	}

	// 如果支付成功且是虚拟商品（会员商品），执行开通会员操作
	if *transaction.TradeState == "SUCCESS" {
		err = e.handleVipActivation(*transaction.OutTradeNo)
		if err != nil {
			global.GVA_LOG.Error("开通会员失败", zap.Error(err))
			// 记录错误但不影响订单状态更新
		}
	}

	return nil
}

// savePaymentCallback 保存支付回调记录
func (e *WxPayService) savePaymentCallback(notifyReq *notify.Request, transaction *PaymentTransaction) error {
	// 将交易信息序列化为JSON
	transactionJSON, err := json.Marshal(transaction)
	if err != nil {
		return fmt.Errorf("序列化交易信息失败: %v", err)
	}

	// 创建回调记录 - 使用PayAction结构体
	callback := model.PayAction{
		ID:           *transaction.TransactionId,
		CreateTime:   time.Now(),
		ResourceType: "encrypt-resource",
		EventType:    "TRANSACTION.SUCCESS",
		Summary:      "支付成功",
	}

	// 设置加密资源信息（这里简化处理，实际应该包含完整的加密信息）
	callback.Resource.OriginalType = "transaction"
	callback.Resource.Ciphertext = string(transactionJSON)

	// 保存到数据库
	err = global.GVA_DB.Create(&callback).Error
	if err != nil {
		return fmt.Errorf("保存回调记录失败: %v", err)
	}

	return nil
}

// handleVipActivation 处理会员开通逻辑
func (e *WxPayService) handleVipActivation(outTradeNo string) error {
	// 查询订单信息
	var orderInfo order.Order
	err := global.GVA_DB.Where("out_trade_no = ?", outTradeNo).First(&orderInfo).Error
	if err != nil {
		return fmt.Errorf("查询订单信息失败: %v", err)
	}

	// 检查是否为虚拟商品（会员商品）
	if orderInfo.ProductType != order.ProductTypeVirtual {
		// 不是虚拟商品，无需开通会员
		return nil
	}

	// 根据商品名称或SKU判断会员类型和天数
	vipType, vipDays := e.getVipTypeAndDays(orderInfo.ProductName, orderInfo.ProductSku)
	if vipType == 0 || vipDays == 0 {
		global.GVA_LOG.Warn("无法确定会员类型或天数", zap.String("productName", *orderInfo.ProductName), zap.String("productSku", *orderInfo.ProductSku))
		return nil
	}

	// 计算会员到期时间
	expiredAt := time.Now().AddDate(0, 0, vipDays)

	// 查询用户是否已有会员记录
	var existingVip user.Vip
	err = global.GVA_DB.Where("uid = ?", orderInfo.UserId).First(&existingVip).Error
	if err != nil && err.Error() != "record not found" {
		return fmt.Errorf("查询用户会员记录失败: %v", err)
	}

	if err != nil && err.Error() == "record not found" {
		// 创建新的会员记录
		newVip := user.Vip{
			Uid:            orderInfo.UserId,
			ExpiredAt:      &expiredAt,
			ApplyForAt:     time.Now(),
			VipType:        vipType,
			Status:         2,                // 已开通
			OpID:           orderInfo.UserId, // 用户自己开通
			OpenPersonType: 3,                // 用户开通
		}
		err = global.GVA_DB.Create(&newVip).Error
		if err != nil {
			return fmt.Errorf("创建会员记录失败: %v", err)
		}
	} else {
		// 更新现有会员记录
		// 如果当前会员未过期，则在现有到期时间基础上延长
		if existingVip.ExpiredAt != nil && existingVip.ExpiredAt.After(time.Now()) {
			expiredAt = existingVip.ExpiredAt.AddDate(0, 0, vipDays)
		}

		updateData := map[string]interface{}{
			"expired_at":       &expiredAt,
			"apply_for_at":     time.Now(),
			"vip_type":         vipType,
			"status":           2, // 已开通
			"op_id":            orderInfo.UserId,
			"open_person_type": 3, // 用户开通
		}
		err = global.GVA_DB.Model(&existingVip).Updates(updateData).Error
		if err != nil {
			return fmt.Errorf("更新会员记录失败: %v", err)
		}
	}

	// 更新用户类型为会员
	err = global.GVA_DB.Model(&user.User{}).Where("id = ?", orderInfo.UserId).Update("types", 2).Error
	if err != nil {
		return fmt.Errorf("更新用户类型失败: %v", err)
	}

	global.GVA_LOG.Info("会员开通成功",
		zap.Uint("userId", orderInfo.UserId),
		zap.Uint("vipType", vipType),
		zap.Time("expiredAt", expiredAt))

	return nil
}

// getVipTypeAndDays 根据商品信息获取会员类型和天数
func (e *WxPayService) getVipTypeAndDays(productName *string, productSku *string) (uint, int) {
	// 这里可以根据实际业务需求来判断会员类型和天数
	// 可以通过商品名称、SKU或者建立商品与会员类型的映射表来实现

	if productName == nil {
		return 0, 0
	}

	// 示例：根据商品名称判断会员类型和天数
	switch {
	case contains(*productName, "月会员") || contains(*productName, "30天"):
		return 1, 30 // 月会员，30天
	case contains(*productName, "季会员") || contains(*productName, "90天"):
		return 2, 90 // 季会员，90天
	case contains(*productName, "半年会员") || contains(*productName, "180天"):
		return 3, 180 // 半年会员，180天
	case contains(*productName, "年会员") || contains(*productName, "365天"):
		return 4, 365 // 年会员，365天
	default:
		// 默认为月会员
		return 1, 30
	}
}

// contains 检查字符串是否包含子字符串（不区分大小写）
func contains(s, substr string) bool {
	return strings.Contains(strings.ToLower(s), strings.ToLower(substr))
}
